<template>
  <div
    class="virtual-grid-container"
    :style="{ height: containerHeight + 'px' }"
    @scroll="handleScroll"
    ref="containerRef"
  >
    <div class="virtual-grid-content" :style="{ height: totalHeight + 'px' }">
      <div
        class="virtual-grid-items"
        :style="{
          transform: `translateY(${offsetY}px)`,
          display: 'grid',
          gridTemplateColumns: `repeat(${columns}, 1fr)`,
          gap: gap + 'px',
          padding: padding + 'px',
        }"
      >
        <div
          v-for="item in visibleItems"
          :key="item.id"
          class="virtual-grid-item"
          :style="{ height: itemHeight + 'px' }"
        >
          <slot :item="item" :index="getItemIndex(item)">
            {{ item.text }}
          </slot>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from "vue";

interface VirtualGridItem {
  id: number;
  text: string;
  [key: string]: any;
}

interface Props {
  items: VirtualGridItem[];
  itemHeight: number;
  containerHeight: number;
  columns: number;
  gap?: number;
  padding?: number;
  bufferSize?: number;
}

const props = withDefaults(defineProps<Props>(), {
  itemHeight: 200,
  containerHeight: 400,
  columns: 3,
  gap: 10,
  padding: 16,
  bufferSize: 2,
});

const containerRef = ref<HTMLElement>();
const scrollTop = ref(0);
let ticking = false;

// 计算每行的高度（包含间距）
const rowHeight = computed(() => props.itemHeight + props.gap);

// 计算总行数
const totalRows = computed(() => Math.ceil(props.items.length / props.columns));

// 计算总高度
const totalHeight = computed(() => totalRows.value * rowHeight.value);

// 计算可见区域
const visibleStartRow = computed(() => {
  const start = Math.floor(scrollTop.value / rowHeight.value);
  return Math.max(0, start - props.bufferSize);
});

const visibleEndRow = computed(() => {
  const end =
    visibleStartRow.value +
    Math.ceil(props.containerHeight / rowHeight.value) +
    props.bufferSize * 2;
  return Math.min(end, totalRows.value);
});

// 计算可见项目
const visibleItems = computed(() => {
  const startIndex = visibleStartRow.value * props.columns;
  const endIndex = visibleEndRow.value * props.columns;
  return props.items.slice(startIndex, endIndex);
});

// 计算偏移量
const offsetY = computed(() => visibleStartRow.value * rowHeight.value);

// 获取项目在原始数组中的索引
const getItemIndex = (item: VirtualGridItem) => {
  return props.items.findIndex((i) => i.id === item.id);
};

// 节流滚动处理
const handleScroll = (event: Event) => {
  if (!ticking) {
    requestAnimationFrame(() => {
      const target = event.target as HTMLElement;
      scrollTop.value = target.scrollTop;
      ticking = false;
    });
    ticking = true;
  }
};

// 滚动到指定位置
const scrollTo = (index: number) => {
  if (containerRef.value) {
    const rowIndex = Math.floor(index / props.columns);
    const scrollTop = rowIndex * rowHeight.value;
    containerRef.value.scrollTop = scrollTop;
  }
};

// 滚动到指定行
const scrollToRow = (rowIndex: number) => {
  if (containerRef.value) {
    const scrollTop = rowIndex * rowHeight.value;
    containerRef.value.scrollTop = scrollTop;
  }
};

// 获取当前可见的行范围
const getVisibleRowRange = () => {
  return {
    start: visibleStartRow.value,
    end: visibleEndRow.value,
  };
};

// 暴露方法给父组件
defineExpose({
  scrollTo,
  scrollToRow,
  getVisibleRowRange,
});
</script>

<style scoped>
.virtual-grid-container {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: auto;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.virtual-grid-content {
  position: relative;
}

.virtual-grid-items {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

.virtual-grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #f0f0f0;
  border-radius: 4px;
  font-size: 14px;
  color: #333;
  background: #fafafa;
  transition: all 0.2s;
  cursor: pointer;
}

.virtual-grid-item:hover {
  background: #f0f0f0;
  border-color: #d0d0d0;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* 响应式设计 */
@media (max-width: 768px) {
  .virtual-grid-items {
    grid-template-columns: repeat(2, 1fr) !important;
  }
}

@media (max-width: 480px) {
  .virtual-grid-items {
    grid-template-columns: 1fr !important;
  }
}
</style>
